package com.tong.support.utils;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;

public class DateUtils {

    public interface Patterns {
        String DATE_TIME_DEFAULT = "yyyy-MM-dd HH:mm:ss";
        String DATE_TIME_NOT_SPLIT = "yyyyMMddHHmmss";
        String DATE_DEFAULT = "yyyy-MM-dd";
        String DATE_CN = "yyyy年MM月dd日";
        String TIME_DEFAULT = "HH:mm:ss";
    }

    public enum Week {
        SUNDAY("星期日"),
        MONDAY("星期一"),
        TUESDAY("星期二"),
        WEDNESDAY("星期三"),
        THURSDAY("星期四"),
        FRIDAY("星期五"),
        SATURDAY("星期六");

        private final String week;

        Week(String week) {
            this.week = week;
        }

        public String getWeek() {
            return week;
        }
    }

    public enum Period {
        YEAR("year"),
        MONTH("month"),
        WEEK("week"),
        DAY("day"),
        HOUR("hour"),
        MINUTE("minute"),
        SECOND("second");
        private final String period;

        Period(String period) {
            this.period = period;
        }

        public String getPeriod() {
            return period;
        }
    }

    public static String[] WEEKS = {Week.MONDAY.getWeek(), Week.TUESDAY.getWeek(), Week.WEDNESDAY.getWeek(), Week.THURSDAY.getWeek(), Week.FRIDAY.getWeek(), Week.SATURDAY.getWeek(), Week.SUNDAY.getWeek()};

    public static String nowDateTime() {
        return format(LocalDateTime.now(), Patterns.DATE_TIME_DEFAULT);
    }

    public static String nowDate() {
        return format(LocalDate.now(), Patterns.DATE_DEFAULT);
    }

    public static String nowTime() {
        return format(LocalTime.now(), Patterns.TIME_DEFAULT);
    }

    public static String nowWeek() {
        return dayOfWeek(LocalDate.now());
    }

    public static String format(TemporalAccessor temporal, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return formatter.format(temporal);
    }

    public static LocalDateTime toLocalDateTime(String str, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDateTime.parse(str, formatter);
    }

    public static LocalDate toLocalDate(String str, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return LocalDate.parse(str, formatter);
    }

    public static LocalDateTime plus(LocalDateTime localDateTime, int num, ChronoUnit unit) {
        return localDateTime.plus(num, unit);
    }

    public static LocalDateTime minus(LocalDateTime localDateTime, int num, ChronoUnit unit) {
        return localDateTime.minus(num, unit);
    }

    public static long diffBetween(LocalDateTime start, LocalDateTime end, ChronoUnit unit) {
        return Math.abs(start.until(end, unit));
    }

    public static long diffBetween(LocalDate start, LocalDate end, ChronoUnit unit) {
        return Math.abs(start.until(end, unit));
    }

    public static String firstDayOfYear() {
        return firstDayOfYear(LocalDateTime.now());
    }

    public static String lastDayOfYear() {
        return lastDayOfYear(LocalDateTime.now());
    }

    public static String firstDayOfYear(LocalDateTime localDateTime) {
        return firstDayOfYear(localDateTime, Patterns.DATE_TIME_DEFAULT);
    }

    public static String firstDayOfYear(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.withDayOfYear(1).withHour(0).withMinute(0).withSecond(0), pattern);
    }

    public static String lastDayOfYear(LocalDateTime localDateTime) {
        return lastDayOfYear(localDateTime, Patterns.DATE_TIME_DEFAULT);
    }

    public static String lastDayOfYear(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.with(TemporalAdjusters.lastDayOfYear()).withHour(23).withMinute(59).withSecond(59), pattern);
    }

    public static String firstDayOfMonth() {
        return firstDayOfMonth(LocalDateTime.now());
    }

    public static String firstDayOfMonth(LocalDateTime localDateTime) {
        return firstDayOfMonth(localDateTime, Patterns.DATE_TIME_DEFAULT);
    }

    public static String firstDayOfMonth(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0), pattern);

    }

    public static String lastDayOfMonth() {
        return lastDayOfMonth(LocalDateTime.now());
    }

    public static String lastDayOfMonth(LocalDateTime localDateTime) {
        return lastDayOfMonth(localDateTime, Patterns.DATE_TIME_DEFAULT);
    }

    public static String lastDayOfMonth(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.with(TemporalAdjusters.lastDayOfMonth()).withHour(23).withMinute(59).withSecond(59), pattern);

    }

    public static String firstDayOfWeek() {
        return firstDayOfWeek(LocalDateTime.now());
    }

    public static String firstDayOfWeek(LocalDateTime localDateTime) {
        return firstDayOfWeek(localDateTime, Patterns.DATE_TIME_DEFAULT);
    }

    public static String firstDayOfWeek(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.with(DayOfWeek.MONDAY).withHour(0).withMinute(0).withSecond(0), pattern);
    }

    public static String lastDayOfWeek() {
        return lastDayOfWeek(LocalDateTime.now());
    }

    public static String lastDayOfWeek(LocalDateTime localDateTime) {
        return lastDayOfWeek(localDateTime, Patterns.DATE_TIME_DEFAULT);
    }

    public static String lastDayOfWeek(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.with(DayOfWeek.SUNDAY).withHour(23).withMinute(59).withSecond(59), pattern);
    }


    public static String dayOfWeek(LocalDate localDate) {
        int dayOfWeek = localDate.getDayOfWeek().getValue() - 1;
        return WEEKS[dayOfWeek];
    }

    public static String startTimeOfDay() {
        return startTimeOfDay(LocalDateTime.now());
    }

    public static String startTimeOfDay(LocalDateTime localDateTime) {
        return startTimeOfDay(localDateTime, Patterns.DATE_TIME_DEFAULT);
    }

    public static String startTimeOfDay(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.withHour(0).withMinute(0).withSecond(0), pattern);
    }


    public static String endTimeOfDay() {
        return endTimeOfDay(LocalDateTime.now());
    }

    public static String endTimeOfDay(LocalDateTime localDateTime) {
        return endTimeOfDay(localDateTime, Patterns.DATE_TIME_DEFAULT);
    }

    public static String endTimeOfDay(LocalDateTime localDateTime, String pattern) {
        return format(localDateTime.withHour(23).withMinute(59).withSecond(59), pattern);
    }


    public static List<String> listDate(String startDate, String endDate, Period period) {
        List<String> result = new ArrayList<>();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Patterns.DATE_DEFAULT);
        LocalDate end = LocalDate.parse(endDate, formatter);
        LocalDate start = LocalDate.parse(startDate, formatter);
        LocalDate tmp = start;

        switch (period) {
            case DAY:
                while (start.isBefore(end) || start.isEqual(end)) {
                    result.add(start.toString());
                    start = start.plusDays(1);
                }
                break;
            case WEEK:
                while (tmp.isBefore(end) || tmp.isEqual(end)) {
                    if (tmp.plusDays(6).isAfter(end)) {
                        result.add(tmp.toString() + "," + end);
                    } else {
                        result.add(tmp.toString() + "," + tmp.plusDays(6));
                    }
                    tmp = tmp.plusDays(7);
                }
                break;
            case MONTH:
                while (tmp.isBefore(end) || tmp.isEqual(end)) {
                    LocalDate lastDayOfMonth = tmp.with(TemporalAdjusters.lastDayOfMonth());
                    if (lastDayOfMonth.isAfter(end)) {
                        result.add(tmp.toString() + "," + end);
                    } else {
                        result.add(tmp.toString() + "," + lastDayOfMonth);
                    }
                    tmp = lastDayOfMonth.plusDays(1);
                }
                break;
            case YEAR:
                while (tmp.isBefore(end) || tmp.isEqual(end)) {
                    LocalDate lastDayOfYear = tmp.with(TemporalAdjusters.lastDayOfYear());
                    if (lastDayOfYear.isAfter(end)) {
                        result.add(tmp.toString() + "," + end);
                    } else {
                        result.add(tmp.toString() + "," + lastDayOfYear);
                    }
                    tmp = lastDayOfYear.plusDays(1);
                }
                break;
            default:
                break;
        }


        return result;
    }

}
